package jscinttools.scint.emissionspectrum;

import jscinttools.scint.emissionspectrum.*;
import hep.aida.IDataPointSet;
import jhplot.P1D;
import jhplot.P0D;

import java.lang.System;
import java.lang.Math;
import java.io.PrintStream;

/**
 *  Static MEthods for Emission Spectra
 *
 *@author     Marc-Antoine Verdier
 *@created    14 avril 2011
 */
public class ModEmSpec
{
	/**
	 *  Constructor for the ModEmSpec object
	 */
	private ModEmSpec() { }


	/**
	 *  Scale the energy (X axis) to an other unit
	 *
	 *@param  enConv    The EnergyScaleConverter to be used
	 *@param  spectrum  The P1D spectrum
	 *@return           The scaled spectrum
	 */
	public static P1D scaleEnergy(P1D spectrum, EnergyScaleConverter enConv)
	{
		return enConv.convertEnergyScale(spectrum);
	}


	/**
	 *  Scale the energy (X axis) to an other unit
	 *
	 *@param  spectrum  The EnergyScaleConverter to be used
	 *@param  enConv    The IDataPointSet spectrum
	 *@return           The scaled spectrum
	 */
	public static IDataPointSet scaleEnergy(IDataPointSet spectrum, EnergyScaleConverter enConv)
	{
		return enConv.convertEnergyScale(spectrum);
	}


	/**
	 *  Remove multiple identical (on X axis) datapoints (the first is kept)
	 *
	 *@param  spectrum  The P1D spectrum
	 *@return           The shortened spectrum
	 */
	public static P1D removeMultipleVal(P1D spectrum)
	{
		int max = spectrum.size();
		P0D xVal = new P0D(spectrum.getArrayX());
		P0D xLeftVal = new P0D(spectrum.getArrayXleft());
		P0D xRightVal = new P0D(spectrum.getArrayXright());
		P0D yVal = new P0D(spectrum.getArrayY());
		P0D yUpperVal = new P0D(spectrum.getArrayYupper());
		P0D yLowerVal = new P0D(spectrum.getArrayYlower());

		for (int i = 0; i < (xVal.size() - 1); i++)
		{
			while (xVal.get(i + 1) == xVal.get(i))
			{
				if (i < (xVal.size() - 2))
				{
					xVal.remove(i + 1);
					xLeftVal.remove(i + 1);
					xRightVal.remove(i + 1);
					yVal.remove(i + 1);
					yUpperVal.remove(i + 1);
					yLowerVal.remove(i + 1);
					max--;
				}
				else
				{
					break;
				}
			}
		}
		if (xVal.get(max - 1) == xVal.get(max - 2))
		{
			xVal.remove(max - 1);
			xLeftVal.remove(max - 1);
			xRightVal.remove(max - 1);
			yVal.remove(max - 1);
			yUpperVal.remove(max - 1);
			yLowerVal.remove(max - 1);
			max--;
		}
		P1D shortSpec = new P1D("Sortened Spectrum");
		for (int j = 0; j < max; j++)
		{
			shortSpec.add(xVal.get(j), yVal.get(j), xLeftVal.get(j), xRightVal.get(j), yUpperVal.get(j), yLowerVal.get(j));
		}
		return shortSpec;
	}


	/**
	 *  Remove multiple identical (on X axis) datapoints (the first is kept)
	 *
	 *@param  spectrum  The IDataPointSet spectrum
	 *@return           The shortened spectrum
	 */
	public static IDataPointSet removeMultipleVal(IDataPointSet spectrum)
	{
		return removeMultipleVal(new P1D(spectrum)).getIDataPointSet();
	}


	/**
	 *  Round to integer the energy values (X axis)
	 *
	 *@param  spectrum  The P1D spectrum
	 *@return           The rounded spectrum
	 */
	public static P1D roundEnergyVal(P1D spectrum)
	{
		P1D roundSpec = new P1D("Rounded Spectrum");
		int max = spectrum.size();
		for (int i = 0; i < max; i++)
		{
			roundSpec.add(Math.round(spectrum.getX(i)), spectrum.getY(i));
		}
		return roundSpec;
	}


	/**
	 *  Round to integer the energy values (X axis)
	 *
	 *@param  spectrum  The IDataPointSet spectrum
	 *@return           The rounded spectrum
	 */
	public static IDataPointSet roundEnergyVal(IDataPointSet spectrum)
	{
		int max = spectrum.size();
		for (int i = 0; i < max; i++)
		{
			spectrum.point(i).coordinate(0).setValue(Math.round(spectrum.point(i).coordinate(0).value()));
		}
		return spectrum;
	}


	/**
	 *  Normalize spectrum to 1 at maximum amplitude
	 *
	 *@param  spectrum  The P1D spectrum
	 *@return           The normalized spectrum
	 */
	public static P1D normalizeAmpSpec(P1D spectrum)
	{
		P1D normSpec = new P1D("Normalized Spectrum");
		double scalFac = spectrum.getMax(1);
		int max = spectrum.size();
		for (int i = 0; i < max; i++)
		{
			normSpec.add(spectrum.getX(i), spectrum.getY(i) / scalFac);
		}
		return normSpec;
	}


	/**
	 *  Normalize spectrum to 1 at maximum amplitude
	 *
	 *@param  spectrum  The IDataPointSet spectrum
	 *@return           The normalized spectrum
	 */
	public static IDataPointSet normalizeAmpSpec(IDataPointSet spectrum)
	{
		double scalFac = spectrum.upperExtent(1);
		int max = spectrum.size();
		for (int i = 0; i < max; i++)
		{
			spectrum.point(i).coordinate(1).setValue(spectrum.point(i).coordinate(1).value() / scalFac);
			spectrum.point(i).coordinate(1).setErrorMinus(spectrum.point(i).coordinate(1).errorMinus() / scalFac);
			spectrum.point(i).coordinate(1).setErrorPlus(spectrum.point(i).coordinate(1).errorPlus() / scalFac);
		}
		return spectrum;
	}


	/**
	 *  Normalize spectrum to intergal = 1
	 *
	 *@param  spectrum  The P1D spectrum
	 *@return           The normalized spectrum
	 */
	public static P1D normalizeAreaSpec(P1D spectrum)
	{
		P1D normSpec = new P1D("Normalized Spectrum");
		double scalFac = integrateSpec(spectrum);
		int max = spectrum.size();
		for (int i = 0; i < max; i++)
		{
			normSpec.add(spectrum.getX(i), spectrum.getY(i) / scalFac);
		}
		return normSpec;
	}


	/**
	 *  Normalize spectrum to intergal = 1
	 *
	 *@param  spectrum  The IDataPointSet spectrum
	 *@return           The normalized spectrum
	 */
	public static IDataPointSet normalizeAreaSpec(IDataPointSet spectrum)
	{
		double scalFac = integrateSpec(spectrum);
		int max = spectrum.size();
		for (int i = 0; i < max; i++)
		{
			spectrum.point(i).coordinate(1).setValue(spectrum.point(i).coordinate(1).value() / scalFac);
			spectrum.point(i).coordinate(1).setErrorMinus(spectrum.point(i).coordinate(1).errorMinus() / scalFac);
			spectrum.point(i).coordinate(1).setErrorPlus(spectrum.point(i).coordinate(1).errorPlus() / scalFac);
		}
		return spectrum;
	}


	/**
	 *  Convolute the spectrum with the PMT QE curve
	 *
	 *@param  spectrum  The P1D spectrum
	 *@param  qe        The P1D QE
	 *@return           The convoluted spectrum
	 */
	public static P1D convoluteQE(P1D spectrum, P1D qe)
	{
		P1D convSpec = new P1D("Convoluted Spectrum", 6);
		int max = spectrum.size();
		int maxqe = qe.size();
		for (int i = 0; i < max; i++)
		{
			for (int j = 0; j < maxqe; j++)
			{
				if (spectrum.getX(i) == qe.getX(j))
				{
					convSpec.add(spectrum.getX(i), spectrum.getY(i) * qe.getY(j), spectrum.getX(i) * (spectrum.getXleft(i) + qe.getXleft(j)), spectrum.getX(i) * (spectrum.getXright(i) + qe.getXright(j)), spectrum.getY(i) * qe.getYupper(j) + qe.getY(j) * spectrum.getYupper(i), spectrum.getY(i) * qe.getYlower(j) + qe.getY(j) * spectrum.getYlower(i));
				}
			}
		}
		return convSpec;
	}


	/**
	 *  Convolute the spectrum with the PMT QE curve
	 *
	 *@param  spectrum  The IDataPointSet spectrum
	 *@param  qe        The P1D QE
	 *@return           The convoluted spectrum
	 */
	public static IDataPointSet convoluteQE(IDataPointSet spectrum, IDataPointSet qe)
	{
		return convoluteQE(new P1D(spectrum), new P1D(qe)).getIDataPointSet();
	}


	/**
	 *  Caluculate the integral value of the spectrum (sum((x+1 - x)*y))
	 *
	 *@param  spectrum  The P1D spectrum
	 *@return           The integral value
	 */
	public static double integrateSpec(P1D spectrum)
	{
		double integ = 0;
		int max = spectrum.size();
		for (int i = 0; i < max - 1; i++)
		{
			integ += Math.abs(spectrum.getX(i + 1) - spectrum.getX(i)) * spectrum.getY(i);
		}
		return integ;
	}


	/**
	 *  Caluculate the integral value of the spectrum (sum((x+1 - x)*y))
	 *
	 *@param  spectrum  The IDataPointSet spectrum
	 *@return           The integral value
	 */
	public static double integrateSpec(IDataPointSet spectrum)
	{
		return integrateSpec(new P1D(spectrum));
	}


	/**
	 *  Scale the intensity (Y axis) with a given scale factor
	 *
	 *@param  spectrum  The P1D spectrum
	 *@param  scalFac   Description of the Parameter
	 *@return           The scaled spectrum
	 */
	public static P1D scaleSpec(P1D spectrum, double scalFac)
	{
		P1D scalSpec = new P1D("Scaled Spectrum");
		int max = spectrum.size();
		for (int i = 0; i < max; i++)
		{
			scalSpec.add(spectrum.getX(i), spectrum.getY(i) * scalFac);
		}
		return scalSpec;
	}


	/**
	 *  Scale the intensity (Y axis) with a given scale factor
	 *
	 *@param  spectrum  The IDataPointSet spectrum
	 *@param  scalFac   Description of the Parameter
	 *@return           The scaled spectrum
	 */
	public static IDataPointSet scaleSpec(IDataPointSet spectrum, double scalFac)
	{
		return scaleSpec(new P1D(spectrum), scalFac).getIDataPointSet();
	}
}

